#include "battle_scene.h"
#include "game_engine.h"
#include <algorithm> //for find, remove
#include "utility.h"

#define ARENA_TOP_Y -50.0
#define ARENA_ROW_HEIGHT 22.0
#define ARENA_FG_SIZE 2048.0
#define ARENA_BG_SIZE 1536.0
#define ARENA_ROWS 6
#define ARENA_END_BOUNDARY (ARENA_FG_SIZE / 4)

using namespace std;

BattleScene::BattleScene(std::string id)
	: RenderTarget(id)
{
	offset = 0.0;
	targetOffset = 0.0;
	rightDangerZone = leftDangerZone = false;
	bgNode = NULL;
	fgNode = NULL;

	//Initialize paths for left and right
	for (int i = 0; i < ARENA_ROWS; i++)
	{
		leftPaths.push_back(std::vector<std::unique_ptr<Dude>>());
		rightPaths.push_back(std::vector<std::unique_ptr<Dude>>());
	}

	Sprite *battleLine = new Sprite("BattleLine.png", true);
	battleLine->getIdComp()->id = "battle_line";
	battleLine->centered = true;
	SDL_Surface *screen = GameEngine::getInstance()->getScreen();
	battleLine->setPosition(screen->w / 2, screen->h / 2);
	battleLine->setDestPosition(screen->w / 2, screen->h / 2);
	battleLine->setRelativeToParent(true);
	battleLine->setZOrder(1000);
	battleLine->setMoveSpeed(115.0);
	addChild(battleLine);
}

BattleScene::~BattleScene()
{
	bgNode = NULL;
	fgNode = NULL;

	//Clears paths and deletes each dude during it (due to nature of unique_ptr<Dude>)
	leftPaths.clear();
	rightPaths.clear();
}

void BattleScene::Heartbeat()
{
	bool anyLeft = false;
	bool anyRight = false;

	std::vector<Dude*> removeDudes;
	std::vector<std::vector<std::unique_ptr<Dude>>>* sides[] = { &leftPaths, &rightPaths };
	double leftXMax = -10000.0, rightXMin = 10000.0;

	//Roll every dude's dice before any takes their turns
	for (int i = 0; i < ARENA_ROWS; i++)
	{
		for (int p = 0; p < 2; p++)
		{
			//Go through each dude in this row on this side
			for (int j = 0; j < sides[p]->at(i).size(); j++)
			{
				Dude *dude = sides[p]->at(i).at(j).get();
				dude->stats.rollX(); //Roll their dice before going
			}
		}
	}

	//Give every dude his turn
	for (int i = 0; i < ARENA_ROWS; i++)
	{
		for (int p = 0; p < 2; p++)
		{
			//Go through each dude in this row on this side
			for (int j = 0; j < sides[p]->at(i).size(); j++)
			{
				Dude *dude = sides[p]->at(i).at(j).get();
				dude->Heartbeat();

				if (dude->side == PLAYER_LEFT)
				{
					if (dude->bounds.right() >= leftXMax)
						leftXMax = dude->bounds.right();
				}
				else
				{
					if (dude->bounds.left() <= rightXMin)
						rightXMin = dude->bounds.left();
				}
			}
		}

	}

	//After every dude has gone, see if anyone died and remove them
	// Update dudes after damage being taken
	for (int i = 0; i < ARENA_ROWS; i++)
	{
		for (int p = 0; p < 2; p++)
		{
			//Go through each dude in this row on this side
			for (int j = 0; j < sides[p]->at(i).size(); j++)
			{
				Dude *dude = sides[p]->at(i).at(j).get();

				if (dude->appearance == DUDE_INVISIBLE)
				{
					RemoveDude(dude);
					j--; //decrement j to not skip past the dude who will now be in this place
				}
				else if (dude->appearance != DUDE_DEAD && dude->stats.life <= 0)
				{
					dude->StartDying();
				}
			}
		}
	}

	//Should left army progress
	if (leftXMax >= -targetOffset - 0.51 && rightXMin > -targetOffset + 0.55)
	{
		targetOffset -= min<double>(rightXMin - leftXMax, 100.0);
	}
	//Should right army progress
	else if (rightXMin <= -targetOffset + 0.51 && leftXMax < -targetOffset - 0.55)
	{
		targetOffset += min<double>(rightXMin - leftXMax, 100.0);
	}

	if (targetOffset > ARENA_END_BOUNDARY * 1.5)
		targetOffset = ARENA_END_BOUNDARY * 1.5;
	if (targetOffset < -ARENA_END_BOUNDARY * 1.5)
		targetOffset = -ARENA_END_BOUNDARY * 1.5;
	
	/*
	DudeTemplate templates[9];
	templates[0].baseResourceName = "Robot Man.png";
	templates[0].stats.setAll(2, 1, 10, 1);
	templates[1].baseResourceName = "Catomic Juggernaut.png";
	templates[1].stats.setAll(4, 2, 30, 1);
	templates[2].baseResourceName = "Ed.png";
	templates[2].stats.setAll(3, 1, 10, 1);
	templates[3].baseResourceName = "Face Hands Monster.png";
	templates[3].stats.setAll(2, 1, 12, 1);
	templates[4].baseResourceName = "Gelatinous Snartle Plop.png";
	templates[4].stats.setAll(1, 1, 9, 1);
	templates[5].baseResourceName = "Giant Eye.png";
	templates[5].stats.setAll(3, 1, 14, 1);
	templates[6].baseResourceName = "Imp.png";
	templates[6].stats.setAll(2, 1, 16, 1);
	templates[7].baseResourceName = "Skrugg.png";
	templates[7].stats.setAll(4, 1, 11, 1);
	templates[8].baseResourceName = "Tormented Soul.png";
	templates[8].stats.setAll(2, 1, 13, 1);
	
	if (GetDudeCountForSide(PLAYER_LEFT) < 25 && rand() % 4 == 0)
	{
		int templateId = rand() % 9;
		Dude* dude = new Dude(*this, PLAYER_LEFT, templates[templateId]);

		if (!AddDude(dude))
		{
			delete dude;
			dude = NULL;
		}
	}
	if (GetDudeCountForSide(PLAYER_RIGHT) < 4 && rand() % 6 == 0)
	{
		int templateId = rand() % 9;
		Dude* dude = new Dude(*this, PLAYER_RIGHT, templates[templateId]);

		if (!AddDude(dude))
		{
			delete dude;
			dude = NULL;
		}
	}
	*/
}

void BattleScene::Update(double timeDelta)
{
	double myTime = timeDelta;

	bool moving = false;
	if (targetOffset < offset)
	{
		myTime *= -1;
		moving = true;
	}
	else if (targetOffset > offset)
	{
		moving = true;
	}

	if (moving)
	{
		offset += myTime * 100.0;

		//If we reached it, place directly at targetOffset
		if (myTime > 0 && offset > targetOffset)
			offset = targetOffset;
		if (myTime < 0 && offset < targetOffset)
			offset = targetOffset;

		//clear danger zone flags, since we are moving and will set them again if needed
		rightDangerZone = leftDangerZone = false;

		//Set battle line at offset, without clipping at edges
		RenderTarget *line = findChildWithId("battle_line");
		if (line != NULL)
		{
			double x, y;
			line->getPosition(x, y);
			SDL_Surface* screen = GameEngine::getInstance()->getScreen();
			line->setDestPosition(screen->w / 2 - targetOffset, y);
		}

		if (offset >= ARENA_FG_SIZE / 4)
		{
			offset = ARENA_FG_SIZE / 4;
			rightDangerZone = true;
		}
		else if (offset <= -(ARENA_FG_SIZE / 4))
		{
			offset = -ARENA_FG_SIZE / 4;
			leftDangerZone = true;
		}
		SDL_Surface* screen = GameEngine::getInstance()->getScreen();
		setPosition(offset, 0);
		if (bgNode != NULL)
		{
			bgNode->setPosition(screen->w / 2.0 - (offset / 2.0), screen->h / 2.0);

		}
	}
	

	//Update my components and children
	RenderTarget::Update(timeDelta);
}

void BattleScene::RemoveDude(Dude* dude)
{
	//If Dude is on a path, get his row, find him and remove him
	if (dude->pathNumber != -1)
	{
		vector<vector<unique_ptr<Dude>>> *paths = GetPathsForSide(dude->side);
		vector<unique_ptr<Dude>> &row = paths->at(dude->pathNumber);

		vector<unique_ptr<Dude>>::iterator dudeIndex = row.end();
		for (vector<unique_ptr<Dude>>::iterator it = row.begin(); it != row.end(); ++it)
		{
			if ((*it).get() == dude)
			{
				dudeIndex = it;
				break;
			}
		}
		
		if (dudeIndex != row.end())
			row.erase(dudeIndex); //Remove from vector and delete dude
	}

}


//Returns whether the dude could be added
bool BattleScene::AddDude(Dude* dude)
{
	dude->InitAppearance();
	if (dude->node != NULL)
	{
		//Do we have a spot to place the dude?
		if (PositionNewDude(dude))
		{
			return true; //Return true if we have added this dude
		}
	}
	return false; //False means we couldn't add him, so he can be deleted now if needed
}

//Finds a path to place new dude on and gets him positioned there
bool BattleScene::PositionNewDude(Dude* dude)
{
	double x, y;
	int i = 0;
	int pathNum = -1;

	//try ten times to place dude at max
	while (i < 10)
	{
		//Choose a random path on battlefield
		pathNum = rand() % ARENA_ROWS;

		y = (double)pathNum * ARENA_ROW_HEIGHT + ARENA_TOP_Y;
		x = GameEngine::getInstance()->getScreen()->w / -2.0 - 100;
		if (dude->side == PLAYER_RIGHT)
			x = -x;
		x -= offset;

		//Get the bounds for this position of the dude to check for collisions
		PD_rect newBounds = dude->bounds;
		PD_vect2 dudeOffset = dude->getBoundsOffset();
		newBounds.setTopLeft(x + dudeOffset.x, y + dudeOffset.y);

		std::vector<std::vector<std::unique_ptr<Dude>>> *paths = GetPathsForSide(dude->side);
		Dude *nextDude = NULL;
		if (paths->at(pathNum).size() > 0) //If anyone on this row, get the back person who I might collide with
			nextDude = paths->at(pathNum).back().get();
		
		if (nextDude != NULL)
		{
			if (!newBounds.collidesWith(nextDude->bounds))
			{
				//Add dude on to back of this row in paths
				paths->at(pathNum).push_back(std::unique_ptr<Dude>(dude)); //Give it control of managing the memory of dude

				break; //We've got a spot to be in
			}
		}
		else //We have an empty path, go there
		{
			paths->at(pathNum).push_back(std::unique_ptr<Dude>(dude)); //Give it control of managing the memory of dude
			break; //We've got a spot to be in
		}

		pathNum = -1;
		i++;
	}

	if (pathNum > -1)
	{
		dude->pathNumber = pathNum; //save path this dude is on
		dude->node->setPosition(x, y);
		dude->node->setZOrder(LAYER_MIDDLE + dude->pathNumber);
		dude->node->setRelativeToParent(true);
		dude->updateBounds();
		fgNode->addChild(dude->node);
		return true;
	}
	return false;
}

void BattleScene::SetArena(std::string resourceName, std::string resourceName2)
{
	//Remove old background and foreground arena images
	RenderTarget* oldArena = findChildWithId("arena_bg");
	if (oldArena != NULL)
	{
		oldArena->removeFromParent(); //Remove from wherever it was
		delete oldArena;
	}
	oldArena = findChildWithId("arena_fg");
	if (oldArena != NULL)
	{
		oldArena->removeFromParent(); //Remove from wherever it was
		delete oldArena;
	}

	//Get screen to center bg and fg
	SDL_Surface* screen = GameEngine::getInstance()->getScreen();

	Sprite* arena = new Sprite(resourceName, false);
	arena->getIdComp()->id = "arena_bg";

	Sprite* arena2 = new Sprite(resourceName2, true);
	arena->getIdComp()->id = "arena_fg";

	arena->setPosition(screen->w / 2, screen->h / 2);
	arena->centered = true;
	arena->setZOrder(LAYER_BG);
	arena->setRelativeToParent(true);
	addChild(arena);

	bgNode = arena;

	arena2->setPosition(screen->w / 2, screen->h / 2);
	arena2->centered = true;
	arena2->setZOrder(LAYER_BG + 10);
	arena2->setRelativeToParent(true);
	addChild(arena2);

	fgNode = arena2;

	offset = targetOffset = 0.0; //clear offset when setting the arena in case they could be different sizes later
}

std::vector<std::vector<std::unique_ptr<Dude>>>* BattleScene::GetPathsForSide(PlayerSide side)
{
	if (side == PLAYER_LEFT)
		return &leftPaths;
	else
		return &rightPaths;
}

std::vector<std::unique_ptr<Dude>>* BattleScene::GetRowForDude(Dude* dude)
{
	if (dude == NULL)
		return NULL;
	if (dude->pathNumber > -1)
	{
		if (dude->side == PLAYER_LEFT && dude->pathNumber < leftPaths.size())
			return &leftPaths.at(dude->pathNumber); //Return pointer to the row
		if (dude->side == PLAYER_RIGHT && dude->pathNumber < rightPaths.size())
			return &rightPaths.at(dude->pathNumber); //Return pointer to the row
	}
	return NULL;
}

int BattleScene::GetDudeCountForSide(PlayerSide side)
{
	std::vector<std::vector<std::unique_ptr<Dude>>>* paths = GetPathsForSide(side);

	int count = 0;

	for (int i = 0; i < ARENA_ROWS; i++)
	{
		count += paths->at(i).size(); //Add count for each row
	}
	return count;
}

//am I the front of my row right now?
bool BattleScene::AtFront(Dude *dude)
{
	if (dude == NULL)
		return false;

	std::vector<std::unique_ptr<Dude>> *row = GetRowForDude(dude);
	if (row != NULL && row->size() > 0)
	{
		if (row->front().get() == dude)
		{
			return true;
		}
	}
	return false;
}

//Get enemy if any of this dude
Dude* BattleScene::GetEnemy(Dude *dude)
{
	double amountToTest = 1;
	//We must be at the front of our row, and already at the boundary where we can't move further
	if (AtFront(dude) && !dude->canMoveForward(amountToTest))
	{
		Dude *opposingDude = NULL;
		//Get the paths for the opposite side, then get the row for this dude
		std::vector<std::unique_ptr<Dude>> &row = GetPathsForSide(get_opposite_side(dude->side))->at(dude->pathNumber);
		//If there are any opposing dudes in the other row
		if (row.size() > 0)
		{
			//Get the front dude in row and return
			opposingDude = row.front().get(); //Get pointer to dude

			//If the opposing dude can't move either and is at the front, he can be attacked
			if (opposingDude->atDestination() && !opposingDude->canMoveForward(amountToTest))
				return opposingDude;
		}
	}
	return NULL;
}

//Get dude in front of this dude
Dude* BattleScene::GetNextDude(Dude *dude)
{
	std::vector<std::unique_ptr<Dude>> *row = GetRowForDude(dude);
	if (row == NULL)
		return NULL;
	for (int i = 0; i < row->size(); i++) //Find dude in row
	{
		if (row->at(i).get() == dude)
		{
			if (i - 1 >= 0)
				return row->at(i-1).get();
		}
	}
	return NULL;
}

//Get the X of the middle boundary
double BattleScene::GetMiddleBoundary()
{
	return -targetOffset;
}

bool BattleScene::findSpotInRow(Dude* dude, vector<unique_ptr<Dude>> *row, int &spot)
{
	if (dude == NULL || row == NULL)
		return false;

	int index = -1;
	bool foundSpot = false;
	PD_rect &bounds = dude->bounds;
	bool reverse = dude->xDir < 0;

	//If no one in row, add at zero
	if (row->size() == 0)
	{
		spot = 0;
		return true;
	}

	//If in front of front person, go in front
	if ((!reverse && row->front()->bounds.right() < bounds.left())
		|| (reverse && row->front()->bounds.left() > bounds.right()))
	{
		spot = 0;
		return true;
	}
	
	//If behind back person, go in back
	if ((!reverse && row->back()->bounds.left() > bounds.right())
		|| (reverse && row->back()->bounds.right() < bounds.left()))
	{
		spot = row->size(); //needs to go at end index (not an index now)
		return true;
	}

	/*
	//Need to implement algorithm to find spot in between people in the row
	for (int i = 0; i < row->size(); i++)
	{
		




	}
	*/
	return false;
}

int BattleScene::FindDudeInRow(Dude *dude, vector<unique_ptr<Dude>> &row)
{
	for (int i = 0; i < row.size(); i++)
	{
		if (row.at(i).get() == dude)
			return i;
	}

	return -1;
}

bool BattleScene::TryMoveSideways(Dude *dude)
{
	if (dude == NULL)
		return false;

	vector<vector<unique_ptr<Dude>>> *paths = GetPathsForSide(dude->side);
	vector<unique_ptr<Dude>> &myRow = paths->at(dude->pathNumber);
	int currentIndex = FindDudeInRow(dude, myRow);
	if (paths != NULL)
	{
		vector<int> pathsToTry;

		//If top half, try down first
		if (dude->pathNumber < 2)
		{
			if (dude->pathNumber + 1 < ARENA_ROWS)
				pathsToTry.push_back(dude->pathNumber + 1);
			if (dude->pathNumber > 0)
				pathsToTry.push_back(dude->pathNumber - 1);
		}
		//If bottom half, try up first
		else
		{
			if (dude->pathNumber > 0)
				pathsToTry.push_back(dude->pathNumber - 1);
			if (dude->pathNumber + 1 < ARENA_ROWS)
				pathsToTry.push_back(dude->pathNumber + 1);
		}

		for (int i = 0; i < pathsToTry.size(); i++)
		{
			int newPathNum = pathsToTry[i];

			vector<unique_ptr<Dude>> *row = &paths->at(newPathNum);
			int foundSpot = -1;
			if (findSpotInRow(dude, row, foundSpot))
			{
				//Insert dude in new row, using move function which makes sure the memory is not cleaned up but transferred
				row->insert(row->begin() + foundSpot, std::move( myRow.at(currentIndex) ));
				//Remove the empty unique_ptr from old row
				myRow.erase( myRow.begin() + currentIndex );
				double mult = (double)newPathNum - dude->pathNumber;
				dude->pathNumber = newPathNum; //Update path number
				//Update the destination position to new row y
				dude->moveBy(0.0, mult * ARENA_ROW_HEIGHT);
				dude->setZ(LAYER_MIDDLE + newPathNum);

				return true;
			}
		}

		/*
		if (dude->pathNumber > 0)
		{
			int newPathNum = dude->pathNumber - 1;
			vector<unique_ptr<Dude>> *row = &paths->at(newPathNum);
			int foundSpot = -1;
			if (findSpotInRow(dude, row, foundSpot))
			{
				//Insert dude in new row, using move function which makes sure the memory is not cleaned up but transferred
				row->insert(row->begin() + foundSpot, std::move( myRow.at(currentIndex) ));
				//Remove the empty unique_ptr from old row
				myRow.erase( myRow.begin() + currentIndex );
				dude->pathNumber = newPathNum; //Update path number
				//Update the destination position to new row y
				dude->moveBy(0.0, -ARENA_ROW_HEIGHT);
				dude->setZ(LAYER_MIDDLE + newPathNum);

				return true;
			}
		}
		if (dude->pathNumber + 1 < ARENA_ROWS)
		{
			int newPathNum = dude->pathNumber + 1;
			vector<unique_ptr<Dude>> *row = &paths->at(newPathNum);
			int foundSpot = -1;
			if (findSpotInRow(dude, row, foundSpot))
			{
				//Insert dude in new row, using move function which makes sure the memory is not cleaned up but transferred
				row->insert(row->begin() + foundSpot, std::move( myRow.at(currentIndex) ));
				//Remove the empty unique_ptr from old row
				myRow.erase( myRow.begin() + currentIndex );
				dude->pathNumber = newPathNum; //Update path number
				//Update the destination position to new row y
				dude->moveBy(0.0, ARENA_ROW_HEIGHT);
				dude->setZ(LAYER_MIDDLE + newPathNum);

				return true;
			}
		}
		*/
	}
	
	return false;
}
